/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.solr.response;

import com.huisou.search.B2bTool;

import org.apache.solr.client.solrj.SolrResponse;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.SolrResponseBase;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.tools.generic.*;

import java.io.*;
import java.util.Properties;

public class VelocityResponseWriter implements QueryResponseWriter {
	
	// TODO: maybe pass this Logger to the template for logging from there?
	// private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class);
	
	@Override
	public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
		VelocityEngine engine = getEngine(request); // 
		
		Template template = getTemplate(engine, request);
		
		VelocityContext context = new VelocityContext();
		
		context.put("request", request);
		
		// Turn the SolrQueryResponse into a SolrResponse.
		// QueryResponse has lots of conveniences suitable for a view
		// Problem is, which SolrResponse class to use?
		// One patch to SOLR-620 solved this by passing in a class name as
		// as a parameter and using reflection and Solr's class loader to
		// create a new instance. But for now the implementation simply
		// uses QueryResponse, and if it chokes in a known way, fall back
		// to bare bones SolrResponseBase.
		// TODO: Can this writer know what the handler class is? With echoHandler=true it can get its string name at least
		SolrResponse rsp = new QueryResponse();
		NamedList<Object> parsedResponse = BinaryResponseWriter.getParsedResponse(request, response);
		try {
			rsp.setResponse(parsedResponse);
			
			// page only injected if QueryResponse works
			context.put("page", new PageTool(request, response)); // page tool only makes sense for a SearchHandler request... *sigh*
		}
		catch (ClassCastException e) {
			// known edge case where QueryResponse's extraction assumes "response" is a SolrDocumentList
			// (AnalysisRequestHandler emits a "response")
			e.printStackTrace();
			rsp = new SolrResponseBase();
			rsp.setResponse(parsedResponse);
		}
		context.put("response", rsp);
		
		// Velocity context tools - TODO: make these pluggable
		context.put("esc", new EscapeTool());
		context.put("date", new ComparisonDateTool());
		context.put("list", new ListTool());
		context.put("math", new MathTool());
		context.put("number", new NumberTool());
		context.put("sort", new SortTool());
		// +++huisou+++
		/* 添加b2b工具 */
		context.put("b2b", new B2bTool());
		// +++huisou+++
		
		context.put("engine", engine); // for $engine.resourceExists(...)
		
		String layout_template = request.getParams().get("v.layout");
		String json_wrapper = request.getParams().get("v.json");
		boolean wrap_response = (layout_template != null) || (json_wrapper != null);
		
		// create output, optionally wrap it into a json object
		if (wrap_response) {
			StringWriter stringWriter = new StringWriter();
			template.merge(context, stringWriter);
			
			if (layout_template != null) {
				context.put("content", stringWriter.toString());
				stringWriter = new StringWriter();
				try {
					// engine.getTemplate(layout_template + ".vm").merge(context, stringWriter);
					// +++huisou+++
					engine.getTemplate(layout_template + ".htm").merge(context, stringWriter);
					// +++huisou+++
				}
				catch (Exception e) {
					throw new IOException(e.getMessage());
				}
			}
			
			if (json_wrapper != null) {
				writer.write(request.getParams().get("v.json") + "(");
				writer.write(getJSONWrap(stringWriter.toString()));
				writer.write(')');
			}
			else { // using a layout, but not JSON wrapping
				writer.write(stringWriter.toString());
			}
		}
		else {
			template.merge(context, writer);
		}
	}
	
	private VelocityEngine getEngine(SolrQueryRequest request) {
		VelocityEngine engine = new VelocityEngine();
		
		engine.setProperty("params.resource.loader.instance", new SolrParamResourceLoader(request));
		SolrVelocityResourceLoader resourceLoader = new SolrVelocityResourceLoader(request.getCore().getSolrConfig().getResourceLoader());
		engine.setProperty("solr.resource.loader.instance", resourceLoader);
		
		File fileResourceLoaderBaseDir = null;
		try {
			String template_root = request.getParams().get("v.base_dir");
			//+++huisou+++
			//fileResourceLoaderBaseDir = new File(request.getCore().getResourceLoader().getConfigDir(), "velocity");
			File instanceDir = new File(request.getCore().getResourceLoader().getInstanceDir());
			fileResourceLoaderBaseDir = new File(instanceDir.getParentFile().getAbsolutePath(), "velocity");
			//+++huisou+++
			if (template_root != null) {
				fileResourceLoaderBaseDir = new File(template_root);
			}
		}
		catch (SolrException e) {
			// no worries... probably in ZooKeeper mode and getConfigDir() isn't available, so we'll just ignore omit
			// the file system resource loader
		}
		
		if (fileResourceLoaderBaseDir != null) {
			engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, fileResourceLoaderBaseDir.getAbsolutePath());
			engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,file,solr");
		}
		else {
			engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,solr");
		}
		
		// TODO: Externalize Velocity properties
		String propFile = request.getParams().get("v.properties");
		try {
			if (propFile == null)
				engine.init();
			else {
				InputStream is = null;
				try {
					is = resourceLoader.getResourceStream(propFile);
					Properties props = new Properties();
					props.load(is);
					engine.init(props);
				}
				finally {
					if (is != null)
						is.close();
				}
			}
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
		
		return engine;
	}
	
	private Template getTemplate(VelocityEngine engine, SolrQueryRequest request) throws IOException {
		Template template;
		
		String template_name = request.getParams().get("v.template");
		String qt = request.getParams().get("qt");
		String path = (String) request.getContext().get("path");
		if (template_name == null && path != null) {
			template_name = path;
		} // TODO: path is never null, so qt won't get picked up maybe special case for '/select' to use qt, otherwise use path?
		if (template_name == null && qt != null) {
			template_name = qt;
		}
		if (template_name == null)
			template_name = "index";
		try {
			// template = engine.getTemplate(template_name + ".vm");
			// +++huisou+++
			template = engine.getTemplate(template_name + ".htm");
			// +++huisou+++
		}
		catch (Exception e) {
			throw new IOException(e.getMessage());
		}
		
		return template;
	}
	
	@Override
	public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
		return request.getParams().get("v.contentType", "text/html;charset=UTF-8");
	}
	
	private String getJSONWrap(String xmlResult) { // TODO: maybe noggit or Solr's JSON utilities can make this cleaner?
		// escape the double quotes and backslashes
		String replace1 = xmlResult.replaceAll("\\\\", "\\\\\\\\");
		replace1 = replace1.replaceAll("\\n", "\\\\n");
		replace1 = replace1.replaceAll("\\r", "\\\\r");
		String replaced = replace1.replaceAll("\"", "\\\\\"");
		// wrap it in a JSON object
		return "{\"result\":\"" + replaced + "\"}";
	}
	
	@Override
	public void init(NamedList args) {
	}
}
